Just dumping it here by hand so I can learn, 99% content here taken from links below:
Ref: Rust by example , Comprehensive rust , Rust Playground
fn main() {
// println!, compile-time code generator before compilation.
println!("Hello world");
}
# compile this file in terminal by running:
rustc main.rs
// singleline comment
/* block level comment */
/// Generate library docs for following item.
//! Generate library docs for enclosing item.
E.g: https://doc.rust-lang.org/rust-by-example/meta/doc.html
Defined macros from std::fmt.
format!: write formatted text to Stringprint!: same as format! but the text is printed to the console (io::stdout).println!: same as print! but a newline is appended.eprint!: same as print! but the text is printed to the standard error (io::stderr).eprintln!: same as eprint! but a newline is appended.println!("day {}", 1);
let pi = 3.1415292;
println!("pi is {pi:.3}")
// 3.142
println!("Base 10: {}", 2); // 2
println!("Base 2: {:b}", 2); // 10 - binary
// b: binary base 2
// o: octal base 8
// x: hexadecimal base 16
println!(
"{0}: what's up? {1} replied, 'Nothing much, what about you? {0}'",
"John", "Susan"
)
println!("{person1}: what's up? {person2} replied, 'Nothing much, what about you? {person1}'", person1="John", person2="Susan")
println!("{:>10}", "John")
// Result, min width of 10 characters including text within:
// John
println!("{name:>5}", name="John")
// John
println!("{10}")
println!("{balance:0>5}", balance=6)
println!("{balance:X<5}", balance=7)
// 000006
// 7XXXXX
println!("{balance:Y<width$}", balance = 7, width = 2);
// 7Y
// or
let width = 2;
println!("{balance:Y<width$}")
println!("Custom {}", Structure(3))
By default, std::fmt only available for the standard implementation, the rest will need to implement it. Start debugging in println by using {value:?}.
// Wouldn't be able to print this one by default - custom type
struct UnPrintable(i32);
// Using default derive method
#[derive(Debug)]
struct Computer<'a> {
name: &'a str,
model: u32,
}
fn main() {
let zephyrus = Computer {
name: "Test",
model: 2014,
};
// Debug Normal print
println!("{zephyrus:?}");
// Debug Pretty print
println!("{zephyrus:#?}");
}
The & indicates a reference, meaning Computer does not own the string but only borrows it. The 'a is a lifetime parameter, ensuring that the reference to the string remains valid for at least the lifetime 'a.
use std::fmt; // Import `fmt`
// A structure holding two numbers. `Debug` will be derived so the results can
// be contrasted with `Display`.
#[derive(Debug)]
struct MinMax(i64, i64);
// Implement `Display` for `MinMax`.
impl fmt::Display for MinMax {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Use `self.number` to refer to each positional data point.
write!(f, "({}, {})", self.0, self.1)
}
}
// Define a structure where the fields are nameable for comparison.
#[derive(Debug)]
struct Point2D {
x: f64,
y: f64,
}
// Similarly, implement `Display` for `Point2D`.
impl fmt::Display for Point2D {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Customize so only `x` and `y` are denoted.
write!(f, "x: {}, y: {}", self.x, self.y)
}
}
use std::fmt;
#[derive(Debug)]
struct Complex {
real: f32,
imag: f32,
}
impl fmt::Display for Complex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{0} + {1}i", self.real, self.imag)
}
}
fn main() {
let c1 = Complex {
real: 3.3,
imag: 7.2,
};
println!("Print output:\n{c1}");
println!("\nDebug output:\n{c1:#?}");
}
/*
Print output:
3.3 + 7.2i
Debug output:
Complex {
real: 3.3,
imag: 7.2,
}
*/
fn test() -> i32 {
2
}
fn main() {
let y = 10;
println!("Hello, world!");
dbg!(test(), dbg!(y) + 10);
}
#[allow(dead_code)] is an attribute which only applies to the module after it.
By default, variable is immutable, mut to let it mutable.
Built-in types and syntax for literal values
| Types | Literals | |
|---|---|---|
| Signed integers | i8, i16, i32, i64, i128, isize | -10, 0, 1_000, 123_i64 |
| Unsigned integers | u8, u16, u32, u64, u128, usize | 0, 123, 10_u16 |
| Floating point numbers | f32, f64 | 3.14, -10.0e20, 2_f32 |
| Unicode scalar values | char | 'a', '$', '∞' |
| Booleans | bool | true, false |
| Types have widths as follows: |
iN, uN, fN are N bits wide,isize and usize are the width of a pointer,char is 32 bits wide,bool is 8 bits wide.let x = 3.14;
let y = 10;
assert_eq!(x, y)
// error: expected floating-point number, found interger.
fn fibo(num: u32) -> u32 {
if num < 2 {
return num;
}
return fibo(num - 1) + fibo(num - 2);
}
fn main() {
let x = 5;
let size = if x > 5 { "big" } : { "small" };
dbg!(size);
// size = 'small'
}
let y = 11;
let size_v2 = match y {
1 => "1",
2 => "2",
5 => {
println!("is eq 5");
"is 5"
}
10 => {
println!("is 10");
"is 10"
}
_ => {
println!("not match");
""
}
};
fn main() {
let mut i: i8 = 1;
while i < 10 {
i += 1;
}
dbg!(i);
}
fn main() {
for x in 1..10 {
println!("{x:>width$}", width = x)
}
for x in [1, 3, 5] {
println!("{x}")
}
for i in (0..10).rev() {
// 9, 8, 7, ... 0
}
// can be 'A'..='Z', 'a'..='z', '0'..='9'
for i in 'A'..='Z' {
// A B C
}
}
loop {
// alway run until "break"
}
fn main() {
let s = [[5, 6, 7], [8, 9, 10], [21, 15, 32]];
let mut elements_searched = 0;
let target_value = 10;
'outer: for i in 0..=2 {
for j in 0..=2 {
elements_searched += 1;
if s[i][j] == target_value {
break 'outer;
}
}
}
dbg!(elements_searched);
}
fn main() {
'aloop: for x in [1, 3, 5] {
let mut count = 0;
'bloop: while count < 4 {
count += 1;
println!("Printing {x}");
if count == 3 {
break 'bloop;
}
if x == 3 {
break 'aloop;
}
}
println!("Print outside");
}
}
Macros are expanded into Rust code during compilation, and can take a variable number of arguments. They are distinguished by a ! at the end. The Rust standard library includes an assortment of useful macros.
println!(format, ..) prints a line to standard output, applying formatting described in std::fmt.format!(format, ..) works just like println! but returns the result as a string.dbg!(expression) logs the value of the expression and returns it.todo!() marks a bit of code as not-yet-implemented. If executed, it will panic.assert!unreachable!eprintln!fn main() {
// u8 refer to type, 5 here refer to size of the array
let mut arr: [u8; 5] = [6, 3, 2, 1, 0];
arr[0] = 5;
println!("Arr : {arr:?}");
}
fn main() {
let arr = [0; 5];
// [0, 0, 0, 0, 0]
}
fn main() {
let tup = (0, 5, 10, 15);
// or
let tup1 : (u8, u8, u8, u8) = (1, 2, 3, 4)
}
fn main() {
let arr = [1, 2, 3, 4, 5];
for item in arr {
for i in 1..item {
println!("i: {i}, item: {item}")
}
println!("Item: {item}");
}
}
fn main() {
let arr = [1, 2, 3, 4, 5];
let [first, second, _third, _fourth, _fifth] = arr;
println!("first: {first}, sec: {second}");
let tup = (true, false);
let (is_true, is_false) = tup;
println!("{is_true}, {is_false}");
}
accessing value without taking ownership of the value which also known as borrowing.
read-only value and referenced data cannot be change
fn main() {
let a = 'A';
let b = 'B';
// refering to the reference using &
let mut x = &a;
x = &b;
// refer back to dereference using: *x
// but cannot do write operation to the original source.
}
can read and also write
fn main() {
let mut a = 'A';
let b = 'B';
let r = &mut a;
*r = 'X';
// now `a` changed into 'X'.
}
fn main() {
let mut point = (1, 2);
let x_coord = &mut point.0;
*x_coord = 20;
println!("point: {point:?}");
// (20, 2)
}
fn main() {
let mut coord = (1, 20);
let x_coor = &mut coord.0;
{
coord.0 = 1;
*x_coor = 3;
dbg!(x_coor);
// we have to scope it like this for borrow to end
// so we can use it.
}
println!("{coord:#?}");
}
// Given the aarray
let a = [1, 2, 3, 4, 5];
let slice_0_2 : [i32] = &a[..3]; // or &a[0..3]
let slice_everything = &a[..];
let slice_before_end = &a[..a.len() - 1];
&str vs String in Rust&strfn foo(s: &str))"hello" is a &'static str)String&str because of heap allocation// &str -> String
let s: &str = "hello";
let owned: String = s.to_string();
// String -> &str
let s: String = String::from("hello");
let slice: &str = &s;
fn main() {
let text_a: &str = "Hello";
let mut text_b: String = String::from("World");
let text_c: String = "Yo".to_string();
text_b.push_str(" #01");
println!("{text_a}: {text_b}, {text_c}");
let text_d = format!("{text_a}: {text_b}, {text_c}").to_ascii_uppercase();
let text_e = &text_d[0..3];
println!("{text_a}: {text_b}, {text_c}, text_d: {text_d}, text_e: {text_e}");
}
println!("{:?}", b"abc");
println!(r#"<a href="link.html">link</a>"#);
Rust enforces a number of rules for references that make them always safe to use. One rule is that references can never be null, making them safe to use without null checks. The other rule we’ll look at for now is that references can’t outlive the data they point to.
fn main() {
let x_ref = {
let x = 10;
&x
};
dbg!(x_ref);
}
struct Person {
name: String,
age: u8,
}
fn describe(person: &Person) {
println!("{} is {} years old", person.name, person.age);
}
fn main() {
let john = Person {
name: "John".to_string(),
age: 12,
};
describe(&john);
}
struct Geometry(u32, u32, u32);
fn main() {
let g1 = Geometry(1, 2, 3);
}
#[derive(Debug)]
enum Direction {
Left,
Right,
}
#[derive(Debug)]
enum PlayerMove {
Pass, // simple
Run(Direction), // tuple variant
Teleport { x: u32, y: u32 }, // struct variant
}
fn main() {
let left = Direction::Left;
let p1: PlayerMove = PlayerMove::Run(Direction::Right);
let p2 = PlayerMove::Pass;
let p3 = PlayerMove::Run(left);
println!("{p1:?}, {p2:?}, {p3:?}");
}
decide which size to store the variable:
#[repr(u32)]
enum Bar {
A, // 0
B = 10000,
C, // 10001
}
fn main() {
println!("A: {}", Bar::A as u32);
println!("B: {}", Bar::B as u32);
println!("C: {}", Bar::C as u32);
}
enum CarryableConcreteItem {
Left,
Right,
}
type Item = CarryableConcreteItem;
// Aliases are more useful with long, complex types:
use std::cell::RefCell;
use std::sync::{Arc, RwLock};
type PlayerInventory = RwLock<Vec<Arc<RefCell<Item>>>>;
Use const when you want compile-time constant values (like math constants, config values).
const DIGEST_SIZE: usize = 3;
const FILL_VALUE: u8 = calculate_fill_value();
// constant function
const fn calculate_fill_value() -> u8 {
if DIGEST_SIZE < 10 { 42 } else { 13 }
}
fn compute_digest(text: &str) -> [u8; DIGEST_SIZE] {
let mut digest = [FILL_VALUE; DIGEST_SIZE];
for (idx, &b) in text.as_bytes().iter().enumerate() {
digest[idx % DIGEST_SIZE] = digest[idx % DIGEST_SIZE].wrapping_add(b);
}
digest
}
fn main() {
let digest = compute_digest("Hello");
println!("digest: {digest:?}");
}
static BANNER: &str = "Welcome to RustOS 3.14";
fn main() {
println!("{BANNER}");
}
Extracting data from structures.
fn takes_tuple(tuple: (char, i32, bool)) {
let a = tuple.0;
let b = tuple.1;
let c = tuple.2;
let (a, b, c) = tuple;
let (_, b, c) = tuple;
let (.., c) = tuple;
let (a, .., c) = tuple;
}
fn main() {
let input = 'x';
match input {
'q' => println!("Quiting."),
'a' | 's' | 'w' | 'd' => println!("Moving"),
'0'..='9' => println!("Numeric"),
key if key.is_lowercase() => println!("Lower case!"),
_ => println!("Unknown!"),
}
}
let opt = Some(123);
match opt {
outer @ Some(inner) => {
println!("outer: {outer:?}, inner: {inner}");
}
None => {}
}
fn main() {
let data = Some(8);
match data {
Some(inner) => println!("Some, {inner}"),
None => println!("Nothing"),
}
match data {
outer @ Some(inner) => println!("Some: {outer:?}, {inner}"),
None => println!("Nothing"),
}
}
below work for primitive value but not complex operation or String.
struct Foo {
x: (u32, u32),
y: u32,
}
#[rustfmt::skip]
fn main() {
let foo = Foo { x: (1, 2), y: 3 };
match foo {
Foo { y: 2, x: i } => println!("y = 2, x = {i:?}"),
Foo { x: (1, b), y } => println!("x.0 = 1, b = {b}, y = {y}"),
Foo { y, .. } => println!("y = {y}, other fields were ignored"),
}
}
For complex type matching.
struct Person {
name: String,
age: u32,
}
fn main() {
let name_john = String::from("John");
let name_sarra = String::from("SARRA");
let john = Person {
name: "SARRA".to_string(),
age: 10,
};
match john {
Person { name, .. } if name == name_john => println!("John-san!"),
Person { name, .. } if name.eq(&name_sarra) => println!("Sarra-san!"),
_ => println!("no idea who"),
}
}
enum Result {
Ok(i32),
Err(String),
}
fn divide_in_two(n: i32) -> Result {
if n % 2 == 0 {
Result::Ok(n / 2)
} else {
Result::Err(format!("cannot divide {n} into two equal parts"))
}
}
fn main() {
let n = 100;
match divide_in_two(n) {
Result::Ok(half) => println!("{n} divided in two is {half}"),
Result::Err(msg) => println!("sorry, an error happened: {msg}"),
}
}
fn divide_by_two(num: i32) -> Option<i32> {
if num % 2 == 0 {
return Some(num / 2);
}
return None;
}
fn main() {
let val = divide_by_two(9);
match val {
Some(inner) => println!("Divisible by two, result is: {inner}"),
None => println!("Cannot be divide by two"),
}
}
use std::{thread::sleep, time::Duration};
fn sleep_for(secs: f32) {
if let Ok(duration) = Duration::try_from_secs_f32(secs) {
let duration_as_sec = duration.as_secs();
println!("Sleeping for {duration_as_sec}");
sleep(duration);
println!("Waking up...");
} else {
println!("Cannot sleep with given value: {secs}");
}
}
fn main() {
sleep_for(2.0);
sleep_for(-4.0);
}
fn main() {
let mut text = String::from("How can I help?");
while let Some(result) = text.pop() {
dbg!(result);
}
}
fn find_hex(optional_text: Option<String>) -> Result<u32, String> {
let Some(text) = optional_text else {
return Err("Not valid text.".to_string());
};
let Some(first_character) = text.chars().next() else {
return Err("Not valid first character".to_string());
};
let Some(result) = first_character.to_digit(32) else {
return Err("Cannot convert".to_string());
};
Ok(result)
}
fn main() {
let text = String::from("n");
let result = find_hex(Some(text));
dbg!(result);
}
Associating functions with types.
Behaviors shared by multiple types.
Parameterizing types on other types.
A tour of Rust's rich standard library.
Function pointers with data
Function pointers with data
← back